package main

import (
	"errors"
	"strconv"

	"arbitrage/persistence"

	"gitee.com/hacker61/go-binance/v2"
	"gitee.com/hacker61/go-binance/v2/futures"
)

type SymbolBookTicker struct {
	Symbol            string
	BinanceBookTicker *binance.WsBookTickerEvent
	FuturesBookTicker *futures.WsBookTickerEvent
	Premium           float64
	AntiPremium       float64
}

func (st *SymbolBookTicker) CalcPremium() {
	if st.BinanceBookTicker == nil || st.FuturesBookTicker == nil {
		st.Premium = -1
		st.AntiPremium = -1
	} else {
		st.Premium = -1
		st.AntiPremium = -1
		futuresBestBidPrice, err := strconv.ParseFloat(st.FuturesBookTicker.BestBidPrice, 64)
		if err != nil {
			Error.Printf("FuturesBookTicker.BestBidPrice is not Float64, err = %v\n", err)
			return
		}
		binanceBestAskPrice, err := strconv.ParseFloat(st.BinanceBookTicker.BestAskPrice, 64)
		if err != nil {
			Error.Printf("BinanceBookTicker.BestAskPrice is not Float64, err = %v\n", err)
			return
		}
		st.Premium = (futuresBestBidPrice - binanceBestAskPrice) / binanceBestAskPrice
		//Debug.Printf("Symbol: %s, BinanceBookTicker: %v, FuturesBookTicker: %v, Premium: %v\n", st.Symbol, st.BinanceBookTicker, st.FuturesBookTicker, st.Premium)
		futuresBestAskPrice, err := strconv.ParseFloat(st.FuturesBookTicker.BestAskPrice, 64)
		if err != nil {
			Error.Printf("FuturesBookTicker.BestAskPrice is not Float64, err = %v\n", err)
			return
		}
		binanceBestBidPrice, err := strconv.ParseFloat(st.BinanceBookTicker.BestBidPrice, 64)
		if err != nil {
			Error.Printf("BinanceBookTicker.BestBidPrice is not Float64, err = %v\n", err)
			return
		}
		st.AntiPremium = (futuresBestAskPrice - binanceBestBidPrice) / binanceBestBidPrice
	}
}

type SymbolBookTickerSlice []SymbolBookTicker

func (st SymbolBookTickerSlice) Len() int {
	return len(st)
}

func (st SymbolBookTickerSlice) Less(i, j int) bool {
	return st[i].Premium < st[j].Premium
}

func (st SymbolBookTickerSlice) Swap(i, j int) {
	st[i], st[j] = st[j], st[i]
}

func (st SymbolBookTickerSlice) SymbolIndex(symbol string) int {
	for k, v := range st {
		if v.Symbol == symbol {
			return k
		}
	}
	return -1
}

type OrderState int
type OrderSubState int

const (
	State_Unknow OrderState = iota // 未知状态，仅在程序启动时使用

	// 固定状态
	State_NotPosition // 未持仓状态，没有套利持仓
	State_Position    // 持仓状态

	// 中间状态
	State_OneCanCreatePosition  // 某个交易对符合开仓条件
	State_OneCanClosePosition   // 某个交易对符合平仓条件
	State_LimitOrderCreated     // 开仓，套利限价单已经下发，尚未判断是否成交
	State_AntiLimitOrderCreated // 平仓，套利限价单已经下发，尚未判断是否成交
)

const (
	SubState_Init OrderSubState = iota // 最初状态

	// 现货先成交的序列
	SubState_BinanceAntiCreated  // 现货成交后，合约尚未成交，现货反向单创建完成
	SubState_BinanceAntiComplete // 现货反向单成交，合约单撤销

	// 合约先成交的序列
	SubState_FuturesAntiCreated  // 合约成交后，现货尚未成交，合约反向单创建完成
	SubState_FuturesAntiComplete // 合约反向单成交，现货单撤销

	// 正常最终状态
	SubState_NormalComplete // 正常情况现货和合约成交
)

type StateMachine struct {
	State    OrderState
	SubState OrderSubState
}

func (sm *StateMachine) ChangeState(state OrderState, opts ...interface{}) (err error) {
	// 1.判断当前状态和传入的状态，是否符合变更规范
	if state == State_Unknow { // 不可传入State_Unknow
		err = errors.New("Cannot change state to unknow")
		return
	}
	if sm.State == State_Unknow {
		if state != State_NotPosition && state != State_Position { // 这两种属于固定状态，程序必须从未知状态先走向固定状态
			err = errors.New("Cannot change state from unknow to semistate")
			return
		}
	} else {
		if sm.State == State_NotPosition {
			if state == State_AntiLimitOrderCreated || state == State_OneCanClosePosition {
				err = errors.New("State change illegal")
				return
			}
		} else if sm.State == State_Position {
			if state == State_LimitOrderCreated || state == State_OneCanCreatePosition {
				err = errors.New("State change illegal")
				return
			}
		} else if sm.State == State_LimitOrderCreated {
			if state == State_AntiLimitOrderCreated {
				err = errors.New("State change illegal")
				return
			}
		} else if sm.State == State_AntiLimitOrderCreated {
			if state == State_LimitOrderCreated {
				err = errors.New("State change illegal")
				return
			}
		}
	}
	if state == State_OneCanCreatePosition {
		if sm.State != State_NotPosition && sm.State != State_OneCanCreatePosition {
			err = errors.New("State change illegal")
			return
		}
	} else if state == State_OneCanClosePosition {
		if sm.State != State_Position && sm.State != State_OneCanClosePosition {
			err = errors.New("State change illegal")
			return
		}
	} else if state == State_LimitOrderCreated {
		if sm.State != State_OneCanCreatePosition {
			err = errors.New("State change illegal")
			return
		}
	} else if state == State_AntiLimitOrderCreated {
		if sm.State != State_OneCanClosePosition {
			err = errors.New("State change illegal")
			return
		}
	}

	Info.Printf("Change State from %v to %v\n", sm.State, state)
	Info.Println(opts)

	// 2.正式变更状态
	if state == State_NotPosition {
		err = sm.changeState2NotPosition()
	} else if state == State_Position {
		if len(opts) < 1 {
			err = errors.New("opts length illegal")
			return
		}
		position := opts[0].(persistence.ArbitragePosition)
		err = sm.changeState2Position(position)
	} else if state == State_OneCanCreatePosition {
		if len(opts) < 1 {
			err = errors.New("opts length illegal")
			return
		}
		sbk := opts[0].(SymbolBookTicker)
		err = sm.changeState2OneCanCreatePosition(sbk)
	} else if state == State_OneCanClosePosition {

	} else if state == State_LimitOrderCreated {
		if len(opts) < 2 {
			err = errors.New("opts length illegal")
			return
		}
		binanceOrderId := opts[0].(int64)
		futuresOrderId := opts[1].(int64)
		err = sm.changeState2LimitOrderCreated(binanceOrderId, futuresOrderId)
	} else if state == State_AntiLimitOrderCreated {
		if len(opts) < 2 {
			err = errors.New("opts length illegal")
			return
		}
		binanceOrderId := opts[0].(int64)
		futuresOrderId := opts[1].(int64)
		err = sm.changeState2AntiLimitOrderCreated(binanceOrderId, futuresOrderId)
	}

	sm.State = state
	//sm.SubState = SubState_Init
	err = sm.ChangeSubState(SubState_Init)

	return
}

func (sm *StateMachine) ChangeSubState(state OrderSubState, opts ...interface{}) (err error) {
	Info.Printf("Change SubState from %v to %v\n", sm.SubState, state)
	Info.Println(opts)
	if state == SubState_Init {
		binanceAntiLimitOrderId = 0
		futuresAntiLimitOrderId = 0
		subStateBinanceExecQty = 0.0
		subStateBinanceExecPrice = 0.0
		subStateFuturesExecQty = 0.0
		subStateFuturesExecPrice = 0.0
		//pBinanceFilledOrder = nil
		//pFuturesOrderTradeUpdate = nil
	} else if state == SubState_BinanceAntiCreated {
		if len(opts) < 3 {
			err = errors.New("opts length illegal")
			return
		}
		binanceAntiLimitOrderId = opts[0].(int64)
		futuresAntiLimitOrderId = opts[1].(int64)
		subStateBinanceExecQty = opts[2].(float64)
		subStateBinanceExecPrice = opts[3].(float64)
		subStateFuturesExecQty = 0.0
		subStateFuturesExecPrice = 0.0
		//pBinanceFilledOrder = opts[1].(*binance.WsExecutionReportEvent)
		//pFuturesOrderTradeUpdate = nil
	} else if state == SubState_FuturesAntiCreated {
		if len(opts) < 2 {
			err = errors.New("opts length illegal")
			return
		}
		binanceAntiLimitOrderId = opts[1].(int64)
		futuresAntiLimitOrderId = opts[0].(int64)
		subStateBinanceExecQty = 0.0
		subStateBinanceExecPrice = 0.0
		subStateFuturesExecQty = opts[2].(float64)
		subStateFuturesExecPrice = opts[3].(float64)
		//pBinanceFilledOrder = nil
		//pFuturesOrderTradeUpdate = opts[1].(*futures.WsOrderTradeUpdate)
	}
	sm.SubState = state

	return
}

func (sm *StateMachine) changeState2NotPosition() (err error) {
	binanceLimitOrderId = 0
	futuresLimitOrderId = 0
	binanceAntiLimitOrderId = 0
	futuresAntiLimitOrderId = 0
	dPremiumStage = 0.0
	if symbolBookTickers == nil {
		symbolBookTickers = make(SymbolBookTickerSlice, 0)
	}
	//if sm.State == State_Position || sm.State == State_AntiLimitOrderCreated {
	//	symbolPosition = persistence.ArbitragePosition{}
	//}

	symbolPosition = persistence.ArbitragePosition{}
	canCreateSymbolBookTicker = SymbolBookTicker{}

	return
}

func (sm *StateMachine) changeState2Position(position persistence.ArbitragePosition) (err error) {
	symbolBookTickers = make(SymbolBookTickerSlice, 0)
	dPremiumStage = 0.0

	binanceLimitOrderId = 0
	futuresLimitOrderId = 0
	binanceAntiLimitOrderId = 0
	futuresAntiLimitOrderId = 0

	if sm.State != State_OneCanClosePosition {
		symbolPosition = position
	}
	return
}

func (sm *StateMachine) changeState2OneCanCreatePosition(sbk SymbolBookTicker) (err error) {
	canCreateSymbolBookTicker = sbk

	return
}

func (sm *StateMachine) changeState2LimitOrderCreated(binanceOrderId, futuresOrderId int64) (err error) {
	binanceLimitOrderId = binanceOrderId
	futuresLimitOrderId = futuresOrderId

	return
}

func (sm *StateMachine) changeState2AntiLimitOrderCreated(binanceOrderId, futuresOrderId int64) (err error) {
	binanceLimitOrderId = binanceOrderId
	futuresLimitOrderId = futuresOrderId

	return
}

func NewStateMachine() *StateMachine {
	sm := new(StateMachine)
	sm.State = State_Unknow
	return sm
}
